//
//  PhotoPreviewCell.swift
//  Created by wang tiao on 2025/4/19.
//


import UIKit
import Photos
import PhotosUI
import Kingfisher

class PreviewBaseCell: UICollectionViewCell {
    var singleTapBlock: (() -> Void)?
    
    var currentImage: UIImage? { nil }
    
    var scrollView: UIScrollView? { nil }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        NotificationCenter.default.addObserver(self, selector: #selector(previewVCScroll), name: PhotoPreviewController.previewVCScrollNotification, object: nil)
    }
    
    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    @objc func previewVCScroll() {}
    
    func willDisplay() {}
    
    func didEndDisplaying() {}
    
    func resizeImageView(imageView: UIImageView, image: UIImage) {
        let size = image.size
        var frame: CGRect = .zero
        
        let viewW = bounds.width
        let viewH = bounds.height
        
        let width = viewW
        
        // video和livephoto没必要处理长图和宽图
        frame.size.width = width
        
        let imageHWRatio = size.height / size.width
        let viewHWRatio = viewH / viewW
        
        if imageHWRatio > viewHWRatio {
            frame.size.height = floor(width * imageHWRatio)
        } else {
            var height = floor(width * imageHWRatio)
            if height < 1 || height.isNaN {
                height = viewH
            }
            frame.size.height = height
        }
        
        imageView.frame = frame
        
        if frame.width < viewW || frame.height < viewH {
            imageView.center = CGPoint(x: viewW / 2, y: viewH / 2)
        }
    }
    
    func animateImageFrame(convertTo view: UIView) -> CGRect {
        return .zero
    }
}

// MARK: local image preview cell

class LocalImagePreviewCell: PreviewBaseCell {
    override var currentImage: UIImage? { preview.image }
    
    override var scrollView: UIScrollView? { preview.scrollView }
    
    lazy var preview: PreviewView = {
        let view = PreviewView()
        view.singleTapBlock = { [weak self] in
            self?.singleTapBlock?()
        }
        return view
    }()
    
    var image: UIImage? {
        didSet {
            preview.image = image
            preview.resetSubViewSize()
        }
    }
    
    var longPressBlock: (() -> Void)?
    
    deinit {
        debugPrint("ZLLocalImagePreviewCell deinit")
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }
    
    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        preview.frame = bounds
    }
    
    private func setupUI() {
        contentView.addSubview(preview)
        
        let longGes = UILongPressGestureRecognizer(target: self, action: #selector(longPressAction(_:)))
        longGes.minimumPressDuration = 0.5
        addGestureRecognizer(longGes)
    }
    
    override func didEndDisplaying() {
        preview.scrollView.zoomScale = 1
    }
    
    override func animateImageFrame(convertTo view: UIView) -> CGRect {
        let rect = preview.scrollView.convert(preview.containerView.frame, to: self)
        return convert(rect, to: view)
    }
    
    @objc func longPressAction(_ ges: UILongPressGestureRecognizer) {
        guard currentImage != nil else {
            return
        }
        
        if ges.state == .began {
            longPressBlock?()
        }
    }
}

// MARK: net image preview cell

class NetImagePreviewCell: LocalImagePreviewCell {
    private lazy var progressView: PhotoLoadProgressView = {
        let view = PhotoLoadProgressView()
        view.isHidden = true
        return view
    }()
    
    var progress: CGFloat = 0 {
        didSet {
            progressView.progress = progress
            progressView.isHidden = progress >= 1
        }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        contentView.addSubview(progressView)
    }
    
    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        bringSubviewToFront(progressView)
        progressView.frame = CGRect(x: bounds.width / 2 - 20, y: bounds.height / 2 - 20, width: 40, height: 40)
    }
    
    override func didEndDisplaying() {
        progressView.isHidden = true
        preview.scrollView.zoomScale = 1
    }
    
    override func animateImageFrame(convertTo view: UIView) -> CGRect {
        let rect = preview.scrollView.convert(preview.containerView.frame, to: self)
        return convert(rect, to: view)
    }
}

// MARK: static image preview cell

class PhotoPreviewCell: PreviewBaseCell {
    override var currentImage: UIImage? { preview.image }
    
    override var scrollView: UIScrollView? { preview.scrollView }
    
    private lazy var preview: PreviewView = {
        let view = PreviewView()
        view.singleTapBlock = { [weak self] in
            self?.singleTapBlock?()
        }
        return view
    }()
    
    var model: PhotoModel! {
        didSet {
            preview.model = model
        }
    }
    
    deinit {
        debugPrint("ZLPhotoPreviewCell deinit")
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }
    
    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        preview.frame = bounds
    }
    
    private func setupUI() {
        contentView.addSubview(preview)
    }
    
    override func didEndDisplaying() {
        preview.scrollView.zoomScale = 1
    }
    
    override func animateImageFrame(convertTo view: UIView) -> CGRect {
        let rect = preview.scrollView.convert(preview.containerView.frame, to: self)
        return convert(rect, to: view)
    }
}


class VideoPreviewCell: PreviewBaseCell {
    override var currentImage: UIImage? {
        return imageView.image
    }
    
    private var player: AVPlayer?
    
    var playerView = UIView()
    
    var playerLayer: AVPlayerLayer?
    
    private lazy var progressView = PhotoLoadProgressView()
    
    lazy var imageView: UIImageView = {
        let view = UIImageView()
        view.clipsToBounds = true
        view.contentMode = .scaleAspectFill
        return view
    }()
    
    private lazy var playBtn: UIButton = {
        let btn = UIButton(type: .custom)
        btn.setImage(UIImage(named: "icon_play_pause"), for: .normal)
        btn.addTarget(self, action: #selector(playBtnClick), for: .touchUpInside)
        return btn
    }()
    
    lazy var singleTapGes: UITapGestureRecognizer = {
        let ges = UITapGestureRecognizer()
        ges.addTarget(self, action: #selector(playBtnClick))
        return ges
    }()
    
    private lazy var syncErrorLabel: UILabel = {
        let attStr = NSMutableAttributedString()
        let attach = NSTextAttachment()
        attach.image = UIImage(named: "zl_videoLoadFailed")
        attach.bounds = CGRect(x: 0, y: -10, width: 30, height: 30)
        attStr.append(NSAttributedString(attachment: attach))
        let errorText = NSAttributedString(
            string: "视频加载失败",
            attributes: [
                NSAttributedString.Key.foregroundColor: UIColor.white,
                NSAttributedString.Key.font: UIFont.systemFont(ofSize: 12)
            ]
        )
        attStr.append(errorText)
        
        let label = UILabel()
        label.attributedText = attStr
        return label
    }()

    var isPlaying: Bool {
        if let player, player.rate != 0 {
            return true
        }
        return false
    }
    
    var model: PhotoModel! {
        didSet {
            configureCell()
        }
    }
    
    deinit {
        cancelDownloadVideo()
        try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }
    
    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        if let originalImage = model.originalImage {
            resizeImageView(imageView: imageView, image: originalImage)
        } else if let thumbnailImage = model.thumbnailImage {
            resizeImageView(imageView: imageView, image: thumbnailImage)
        }
        
        playerView.frame = imageView.frame
        playerLayer?.frame = playerView.bounds
        playBtn.frame = CGRect(origin: .zero, size: CGSize(width: 50, height: 50))
        playBtn.center = CGPoint(x: bounds.midX, y: bounds.midY)
        syncErrorLabel.frame = CGRect(x: 10, y: bounds.height / 2 - 17, width: bounds.width - 20, height: 35)
        progressView.frame = CGRect(x: bounds.width / 2 - 30, y: bounds.height / 2 - 30, width: 60, height: 60)
    }
    
    override func previewVCScroll() {
        pausePlayer(seekToZero: false)
    }
    
    override func willDisplay() {
        fetchVideo()
    }
    
    override func didEndDisplaying() {
        imageView.isHidden = false
        NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem)
        
        cancelDownloadVideo()
    }
    
    override func animateImageFrame(convertTo view: UIView) -> CGRect {
        return convert(imageView.frame, to: view)
    }
    
    private func setupUI() {
        contentView.addSubview(playerView)
        contentView.addSubview(imageView)
        contentView.addSubview(syncErrorLabel)
        contentView.addSubview(progressView)
        contentView.addSubview(playBtn)
        contentView.addGestureRecognizer(singleTapGes)
        
        NotificationCenter.default.addObserver(self, selector: #selector(appWillResignActive), name: UIApplication.willResignActiveNotification, object: nil)
    }
    
    private func configureCell() {
        imageView.image = nil
        imageView.isHidden = false
        syncErrorLabel.isHidden = true
        playBtn.isEnabled = false
        player = nil
        if playerLayer?.superlayer != nil {
            playerLayer?.removeFromSuperlayer()
        }
        playerLayer = nil
        
        // 视频预览图尺寸
        var size = model.previewSize
        size.width /= 2
        size.height /= 2
        
        if let originalImage = model.originalImage {
            
            resizeImageView(imageView: imageView, image: originalImage)
            self.imageView.image = originalImage
            
        } else if  let originalPath = model.filePath, let originalImage = UIImage(contentsOfFile: originalPath) {
            
            resizeImageView(imageView: imageView, image: originalImage)
            
            model.originalImage = originalImage
            self.imageView.image = originalImage
            
        } else if let thumbnailImage = model.thumbnailImage {
            
            resizeImageView(imageView: imageView, image: thumbnailImage)
            self.imageView.image = thumbnailImage
            
        } else if let thumbnailPath = model.thumbnailPath, let thumbnailImage = UIImage(contentsOfFile: thumbnailPath) {
            
            model.thumbnailImage = thumbnailImage
            resizeImageView(imageView: imageView, image: thumbnailImage)
            self.imageView.image = thumbnailImage
        }
    }
    
    private func fetchVideo() {
        if let filePath = model.filePath, FileManager.default.fileExists(atPath: filePath) {
            let item = AVPlayerItem(url: URL(fileURLWithPath: filePath))
            self.configurePlayerLayer(item)
        } else {
            if let url = model.url, let URL = URL(string: url) {
                let item = AVPlayerItem(url: URL)
                self.configurePlayerLayer(item)
            }
        }
    }
    
    private func configurePlayerLayer(_ item: AVPlayerItem) {
        playBtn.setImage(UIImage(named: "icon_play_pause"), for: .normal)
        playBtn.isEnabled = true
        
        NotificationCenter.default.removeObserver(self, name: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem)
        
        player = AVPlayer(playerItem: item)
        if playerLayer?.superlayer != nil {
            playerLayer?.removeFromSuperlayer()
            playerLayer = nil
        }
        playerLayer = AVPlayerLayer(player: player)
        playerLayer?.videoGravity = .resizeAspect
        playerLayer?.frame = playerView.bounds
        playerView.layer.insertSublayer(playerLayer!, at: 0)
        
        NotificationCenter.default.addObserver(self, selector: #selector(playFinish), name: AVPlayerItem.didPlayToEndTimeNotification, object: player?.currentItem)
 
    }
    
    @objc private func playBtnClick() {
        let currentTime = player?.currentItem?.currentTime()
        let duration = player?.currentItem?.duration
        if !isPlaying {
            if currentTime?.value == duration?.value {
                player?.currentItem?.seek(to: CMTimeMake(value: 0, timescale: 1), completionHandler: nil)
            }
            imageView.isHidden = true
            try? AVAudioSession.sharedInstance().setCategory(.playback)
            try? AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
            player?.play()
            playBtn.setImage(nil, for: .normal)
            singleTapBlock?()
        } else {
            pausePlayer(seekToZero: false)
        }
    }
    
    @objc private func playFinish() {
        pausePlayer(seekToZero: true, ignorePlayStatus: true)
    }
    
    @objc private func appWillResignActive() {
        pausePlayer(seekToZero: false)
    }
    
    /// 暂停播放器
    /// - Parameters:
    ///   - seekToZero: 是否seek到0秒
    ///   - ignorePlayStatus: 是否忽略当前播放器播放状态（
    /// - Note: 由于`iOS16`后，收到`AVPlayerItem.didPlayToEndTimeNotification`通知后，`player`的`rate`值已经是`0`，所以会被`guard isPlaying else { return }`拦截。所以加了`ignorePlayStatus`参数
    private func pausePlayer(seekToZero: Bool, ignorePlayStatus: Bool = false) {
        guard isPlaying || ignorePlayStatus else { return }
        
        player?.pause()
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
        }
        
        if seekToZero {
            player?.seek(to: .zero)
        }
        
        playBtn.setImage(UIImage(named: "icon_play_pause"), for: .normal)
        singleTapBlock?()
    }
    
    private func cancelDownloadVideo() {
   
    }
}


class PreviewView: UIView {
    private static let defaultMaxZoomScale: CGFloat = 3
    
    private lazy var progressView = PhotoLoadProgressView()
    
    lazy var scrollView: UIScrollView = {
        let view = UIScrollView()
        view.maximumZoomScale = PreviewView.defaultMaxZoomScale
        view.minimumZoomScale = 1
        view.isMultipleTouchEnabled = true
        view.delegate = self
        view.showsHorizontalScrollIndicator = false
        view.showsVerticalScrollIndicator = false
        view.delaysContentTouches = false
        view.contentInsetAdjustmentBehavior = .never;
        return view
    }()
    
    lazy var containerView = UIView()
    
    lazy var imageView: UIImageView = {
        let view = UIImageView()
        view.contentMode = .scaleAspectFill
        view.clipsToBounds = true
        return view
    }()
    
    var image: UIImage? {
        get {
            return imageView.image
        }
        set {
            imageView.image = newValue
        }
    }
    
    var singleTapBlock: (() -> Void)?
    
    var doubleTapBlock: (() -> Void)?
    
    var model: PhotoModel! {
        didSet {
            self.configureView()
        }
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }
    
    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        scrollView.frame = bounds
        progressView.frame = CGRect(x: bounds.width / 2 - 20, y: bounds.height / 2 - 20, width: 40, height: 40)
        scrollView.zoomScale = 1
        resetSubViewSize()
    }
    
    private func setupUI() {
        addSubview(scrollView)
        scrollView.addSubview(containerView)
        containerView.addSubview(imageView)
        addSubview(progressView)
        
        let singleTap = UITapGestureRecognizer(target: self, action: #selector(singleTapAction(_:)))
        scrollView.addGestureRecognizer(singleTap)
        
        let doubleTap = UITapGestureRecognizer(target: self, action: #selector(doubleTapAction(_:)))
        doubleTap.numberOfTapsRequired = 2
        scrollView.addGestureRecognizer(doubleTap)
        
        singleTap.require(toFail: doubleTap)
    }
    
    @objc private func singleTapAction(_ tap: UITapGestureRecognizer) {
        singleTapBlock?()
    }
    
    @objc private func doubleTapAction(_ tap: UITapGestureRecognizer) {
        let scale = scrollView.zoomScale != scrollView.minimumZoomScale ? 1 : scrollView.maximumZoomScale
        let tapPoint = tap.location(in: scrollView)
        var rect = CGRect.zero
        rect.size.width = scrollView.frame.width / scale
        rect.size.height = scrollView.frame.height / scale
        rect.origin.x = tapPoint.x - (rect.size.width / 2)
        rect.origin.y = tapPoint.y - (rect.size.height / 2)
        scrollView.zoom(to: rect, animated: true)
    }
    
    private func configureView() {
        
        scrollView.zoomScale = 1
        
        loadPhoto()
    }
    
    private func requestPhotoSize(gif: Bool) -> CGSize {
        // gif 情况下优先加载一个小的缩略图
        var size = model.previewSize
        if gif {
            size.width /= 2
            size.height /= 2
        }
        return size
    }
    
    private func loadPhoto() {
        
        if let originalImage =  model.originalImage {
            self.imageView.image = originalImage
            self.resetSubViewSize()
            return
        }
        
       if let originalPath = model.filePath, let originalImage = UIImage(contentsOfFile: originalPath) {
           model.originalImage = originalImage
           self.imageView.image = originalImage
           self.resetSubViewSize()
            return
        }
        
        if let thumbnailImage = model.thumbnailImage {
            
            self.imageView.image = thumbnailImage
            self.resetSubViewSize()
            self.downloadImage()
            
        } else {
            
            if let thumbnailPath = model.thumbnailPath, let thumbnailImage = UIImage(contentsOfFile: thumbnailPath) {
                model.thumbnailImage = thumbnailImage
                self.imageView.image = thumbnailImage
                self.resetSubViewSize()
            }
            
            self.downloadImage()
        }
    
    }
    
    
    func downloadImage() {
        
        if let url = model.url, let URL = URL(string: url) {
            
            let resource = KF.ImageResource(downloadURL: URL)
            
            KingfisherManager.shared.retrieveImage(
                with: resource,
                options: nil,
                progressBlock: { receivedSize, totalSize in
                    let progress = Double(receivedSize) / Double(totalSize)
                    print("下载进度：\(progress * 100)%")
                    
                    DispatchQueue.main.async {
                        self.progressView.progress = progress
                        if progress >= 1 {
                            self.progressView.isHidden = true
                        } else {
                            self.progressView.isHidden = false
                        }
                    }
                    
                },
                completionHandler: { result in
                    switch result {
                    case .success(let value):
                        print("图片下载成功，大小：\(value.image.size)")
                        // value.image 就是 UIImage，你可以手动设置到别的地方
                        DispatchQueue.main.async {
                            self.imageView.image = value.image
                            self.model.originalImage = value.image
                            self.resetSubViewSize()
                            self.progressView.isHidden = true
                        }
                    case .failure(let error):
                        print("图片下载失败：\(error)")
                    }
                }
            )
        }
        
    }
    
    func resetSubViewSize() {
        let size = imageView.image?.size ?? bounds.size
        
        var frame: CGRect = .zero
        
        let viewW = bounds.width
        let viewH = bounds.height
        
        let width = viewW
        
        frame.size.width = width
        
        let imageHWRatio = size.height / size.width
        let viewHWRatio = viewH / viewW
        
        if imageHWRatio > viewHWRatio {
            // 长图
            frame.size.width = min(size.width, viewW)
            frame.size.height = floor(frame.size.width * imageHWRatio)
        } else {
            var height = floor(frame.size.width * imageHWRatio)
            if height < 1 || height.isNaN {
                height = viewH
            }
            frame.size.height = height
        }
        
        // 优化 scroll view zoom scale
        if frame.width < frame.height {
            scrollView.maximumZoomScale = max(PreviewView.defaultMaxZoomScale, viewW / frame.width)
        } else {
            scrollView.maximumZoomScale = max(PreviewView.defaultMaxZoomScale, viewH / frame.height)
        }
        
        containerView.frame = frame
        
        if frame.height < viewH {
            containerView.center = CGPoint(x: viewW / 2, y: viewH / 2)
        } else {
            containerView.frame = CGRect(origin: CGPoint(x: (viewW - frame.width) / 2, y: 0), size: frame.size)
        }
        
        MainAsync(after: 0.01) {
            self.scrollView.contentSize = frame.size
            self.imageView.frame = self.containerView.bounds
            self.scrollView.contentOffset = .zero
        }
    }
    
    func resumeGif() {
        guard let m = model else { return }
        guard  m.type == .gif else { return }
        
        guard imageView.layer.speed != 1 else { return }
        
        let pauseTime = imageView.layer.timeOffset
        imageView.layer.speed = 1
        imageView.layer.timeOffset = 0
        imageView.layer.beginTime = 0
        let timeSincePause = imageView.layer.convertTime(CACurrentMediaTime(), from: nil) - pauseTime
        imageView.layer.beginTime = timeSincePause
    }
    
    func pauseGif() {
        guard let m = model else { return }
        guard  m.type == .gif else { return }
        
        guard imageView.layer.speed != 0 else { return }
        
        let pauseTime = imageView.layer.convertTime(CACurrentMediaTime(), from: nil)
        imageView.layer.speed = 0
        imageView.layer.timeOffset = pauseTime
    }
}

extension PreviewView: UIScrollViewDelegate {
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return containerView
    }
    
    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        let offsetX = (scrollView.frame.width > scrollView.contentSize.width) ? (scrollView.frame.width - scrollView.contentSize.width) * 0.5 : 0
        let offsetY = (scrollView.frame.height > scrollView.contentSize.height) ? (scrollView.frame.height - scrollView.contentSize.height) * 0.5 : 0
        containerView.center = CGPoint(x: scrollView.contentSize.width * 0.5 + offsetX, y: scrollView.contentSize.height * 0.5 + offsetY)
    }
    
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        resumeGif()
    }
}
